import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;



/**
 * Extract the WIN channel block. The format of the channel block is like following.
 * offset: value
 * 0-1   : channel number (ID)
 * 2-2.5 : sample size (encode) 0:0.5byte , 1:1byte, 2:2byte, 3:3byte,4:4byte ,other not defined
 * 2.5-3 : sampling rate(Hz). this may be 1-4095. example 100=100Hz
 * 4-7   : first data of the channel. first data always 4 byte long
 * 8-    : 2nd or later data of the channel. this data represent the difference value from previous data.
 *       * number of data would be equal to the sampling rate.
 *        	
 * @author ikechan
 *
 */
public class ChannelData {
	
	private int ChannelName;
	private int samplerate;
	private int encode;
	private int[] Data=null;
//	private long SwapPointer=0;
//	private int  SwapSize=0;
	private int DataLength;
	private wintime Time;
	private int min;
	private int max;
	//private int mean;
	private double meandouble;
	private double variance;
	//-----------------------------------------------------
	// Disk ��purge����
//	public int purgeData(){
//		if(Data==null) return 0; // purge�ł��Ȃ�
//		if(SwapSize!=0){ // ���ł�purge�ς�
//			Data = null;
//			return SwapSize;
//		}
//		String filename = String.format("swap.%04x", ChannelName);
//		RandomAccessFile f;
//		try {
//			f = new RandomAccessFile(filename,"rw");
//			SwapPointer = f.length();
//			f.seek(SwapPointer);
//			byte[] buf = new byte[Data.length*4];
//			for(int i=0;i<Data.length;i++){
//				buf[i*4] = (byte)(0xff & (Data[i] >> 24));
//				buf[i*4+1] = (byte)(0xff & (Data[i] >> 16));
//				buf[i*4+2] = (byte)(0xff & (Data[i] >> 8));
//				buf[i*4+3] = (byte)(0xff & (Data[i] >> 0));
//			}
//			f.write(buf);
//			SwapSize = buf.length;
//			buf = null; Data = null;
//			f.close();
//			//System.out.println(String.format("purge: %dbyte p=%d%s",SwapSize,SwapPointer,filename));
//			
//		} catch (FileNotFoundException e) {
//			e.printStackTrace();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		return SwapSize;
//	}
	public int getmax(){
		return max;
	}
	public int getmin(){
		return min;
	}
	public int getChannelName() {
		return ChannelName;
	}
	public int[] getData() {
//		if(Data == null){
//			if(SwapSize==0) return null;
//			String filename = String.format("swap.%04x", ChannelName);
//			RandomAccessFile f;
//			try {
//				//System.out.println(String.format("restore data %dbyte p=%d %s",SwapSize,SwapPointer,filename));
//				f = new RandomAccessFile(filename,"r");
//				f.seek(SwapPointer);
//				Data = new int[SwapSize];
//				byte[] buf = new byte[SwapSize];
//				f.read(buf);
//				f.close();
//				for(int i=0;i<SwapSize/4;i++){
//					Data[i] = (int)((long)buf[i*4+0]<< 24 | (long)buf[i*4+1] << 16 | (long)buf[i*4+2] << 8 | (long)buf[i*4+3]);
//				}
//				buf = null;
//			} catch (FileNotFoundException e) {
//				e.printStackTrace();
//			} catch (IOException e) {
//				e.printStackTrace();
//			}
//			
//		}
		return Data;
	}
	public int getEncode() {
		return encode;
	}
	public int getSamplerate() {
		return samplerate;
	}
	public int length(){
		return 4 + DataLength;
	}


	public wintime getTime(){
		return Time;
	}
	public int getMax(){
		return max;
	}
	public int getMin(){
		return min;
	}
	public double getMean(){
		return meandouble;
	}
	public double getVariance(){
		return variance;
	}
	ChannelData(){
		
	}
	ChannelData(ChannelData cd){
		ChannelName = cd.ChannelName;
		samplerate = cd.samplerate;
		encode = cd.encode;
		DataLength = cd.DataLength;
		Data = cd.Data;
		Time = new wintime(cd.Time);
		min = cd.min;
		max = cd.max;
		meandouble = cd.meandouble;
		variance = cd.variance;
	}
	ChannelData(byte[] c,int offset,wintime tm,int filetype){
		switch(filetype){
		case 0: 
			makeDataA0(c,offset,tm);
			break;
		case 5:
			makeDataA5(c,offset,tm);
			break;
		}
	}
	ChannelData(byte[] c,int offset,wintime tm){
		makeDataA0(c,offset,tm);
	}
	private void makeDataA0(byte[] c,int offset,wintime tm){
		long lm=0;
		Time = tm;
		int index = offset;
		// channel header decode
		ChannelName = unsigned_byte.convShort(c[index])*0x100 + unsigned_byte.convShort(c[index+1]);
		encode = (0xf0 & (unsigned_byte.convShort(c[index+2])))>>4;
		samplerate = 0xfff & (unsigned_byte.convShort(c[index+2])*0x100 + unsigned_byte.convShort(c[index+3]));
		index += 4;
		
		// how many data  
		if(encode == 0){	// 0.5byte 
			DataLength = 4 + samplerate / 2;
		}
		else{	// 1byte,2byte,3byte,4byte
			DataLength = 4 + (samplerate-1) * encode;
		}
		//System.out.println(String.format("ch=%04x enc=%d samp=%d",ChannelName,encode,samplerate));
		// win data decoding ...
		if(samplerate <= 0){
			return;
		}
		Data = new int[samplerate];
		int d = byteUtil.valueOf4Byte(c,index);
		min = max = d;
		lm = d;
		variance = 0.0;
		index += 4;
		
		Data[0]=d;
		boolean hi = true;
		for(int i=1;i<samplerate;i++){
			int delta;
			switch(encode){
			case 4:		// 4byte
				delta = byteUtil.valueOf4Byte(c,index);
				index += 4;
				d += delta;
				break;
			case 3:		// 3byte
				delta = byteUtil.valueOf3Byte(c,index);
				index += 3;
				d += delta;
				break;
			case 2:		// 2byte
				delta = byteUtil.valueOf2Byte(c,index);
				index += 2;
				d += delta;
				break;
			case 1:		// 1byte
				delta = byteUtil.valueOf1Byte(c,index);
				index += 1;
				d += delta;
				break;
			case 0:		// 0.5byte
				delta = byteUtil.valueOfHalfByte(c,index,hi);
				if(hi){
					hi = false;
				}
				else{
					hi = true;
					index += 1;
				}
				d += delta;
				break;
			}
			Data[i]=d;
			if(min > d) min = d;
			if(max < d) max = d; 
			lm += d;
		}
		meandouble = (double)lm/samplerate;
		
		double val=0.0;
		for(int j=0;j<samplerate;j++){
			val += (Data[j]-meandouble) * (Data[j]-meandouble);
		}
		if(samplerate >1) variance = val /(samplerate-1); 
		else variance = val;
	}

	private void makeDataA5(byte[] c,int offset,wintime tm){
//		long lm=0;
		Time = tm;
		int index = offset;
		// channel header decode
		ChannelName = unsigned_byte.convShort(c[index])*0x100 + unsigned_byte.convShort(c[index+1]);
		encode = (0xf0 & (unsigned_byte.convShort(c[index+2])))>>4;
		int	sampleNum = 0xfff & (unsigned_byte.convShort(c[index+2])*0x100 + unsigned_byte.convShort(c[index+3]));
		samplerate = sampleNum*10;
		index += 4;
		
		// how many data  
		if(encode == 0){	// 0.5byte 
			DataLength = 4 + sampleNum / 2;
		}
		else{	// 1byte,2byte,3byte,4byte
			DataLength = 4 + (sampleNum-1) * encode;
		}
		//System.out.println(String.format("ch=%04x enc=%d samp=%d",ChannelName,encode,sampleNum));
		// win data decoding ...
		if(sampleNum <= 0){
			return;
		}
		Data = new int[sampleNum];
		int d = byteUtil.valueOf4Byte(c,index);
		min = max = d;
//		lm = d;
		variance = 0.0;
		index += 4;
		
		Data[0]=d;
		boolean hi = true;
		for(int i=1;i<sampleNum;i++){
			int delta;
			switch(encode){
			case 4:		// 4byte
				delta = byteUtil.valueOf4Byte(c,index);
				index += 4;
				d += delta;
				break;
			case 3:		// 3byte
				delta = byteUtil.valueOf3Byte(c,index);
				index += 3;
				d += delta;
				break;
			case 2:		// 2byte
				delta = byteUtil.valueOf2Byte(c,index);
				index += 2;
				d += delta;
				break;
			case 1:		// 1byte
				delta = byteUtil.valueOf1Byte(c,index);
				index += 1;
				d += delta;
				break;
			case 0:		// 0.5byte
				delta = byteUtil.valueOfHalfByte(c,index,hi);
				if(hi){
					hi = false;
				}
				else{
					hi = true;
					index += 1;
				}
				d += delta;
				break;
			}
			Data[i]=d;
//			if(min > d) min = d;
//			if(max < d) max = d; 
//			lm += d;
		}
//		meandouble = (double)lm/sampleNum;
//		double val = 0.0;
//		for(int j=0;j<sampleNum;j++){
//			val += (Data[j]-meandouble) * (Data[j]-meandouble);
//		}
//		if(samplerate >1) variance = val /(sampleNum-1); 
//		else variance = val;
		setStatistics();
	}
	
	// max min valiance���Z�b�g����
	public void setStatistics(){
		min = Integer.MAX_VALUE;
		max = Integer.MIN_VALUE;
		long sum = 0;
		for(int i=0;i<Data.length;i++){
			int d = Data[i];
			if(d < min) min = d;
			if(d > max) max = d;
			sum += d;
		}
		meandouble = (double)sum/Data.length;
		double val = 0.0;
		for(int j=0;j<Data.length;j++){
			val += (Data[j]-meandouble) * (Data[j]-meandouble);
		}
		if(Data.length >1) variance = val /(Data.length-1); 
		else variance = val;
		
	}
	public static ChannelData assembleChannelData(ArrayList<ChannelData> cd){
		ChannelData d = new ChannelData(cd.get(0));
		
		int datasize = 0;
		for(int i=0;i<cd.size();i++){
			datasize += cd.get(i).Data.length;
		}
		int[] dat = new int[datasize];

		int j=0;
		for(int k=0;k<cd.size();k++){
			int i=0;
			for(;i<cd.get(k).Data.length;i++){
				int data = cd.get(k).Data[i];
				dat[j++] = data;
			}
		}
		d.Data = dat;
		d.setStatistics();
		return d;
	}
	
}
